// HSS.Forms.ConnectionStringBuilder.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       ConnectionStringBuilder.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/24/2010
// ----------------------------------------------------------------------------
namespace HSS.Forms
{
	#region Using Directives
	using System;
	using System.Data.SqlClient;
	using System.Windows.Forms;
	using HSS.Data;
	using System.Threading;
	using System.Collections.Generic;
	#endregion

	#region ConnectionStringBuilder
	/// <summary>
	/// A dialog for creating an sql database connection string.
	/// </summary>
	public sealed partial class ConnectionStringBuilder : Form, IConfigurationDataEditor<DataAccessConnection>
	{
		#region Fields

		private static readonly string LOCAL_MACHINE = "127.0.0.1";

		private string name;
		private string unmasked;
		private string password;
		private string application;
		private string workStationId;

		private decimal connTimeout = 0;
		private decimal cmdTimeout = 60;

		private bool loading;
		//private bool buildDropDown;

		//private string DEFAULT_DATABASE;

		private Thread uiThread;
		private Thread dbThread;
		private List<object> foundDatabases;

		#endregion

		#region Constructors
		/// <summary>
		/// Construct
		/// </summary>
		public ConnectionStringBuilder()
		{
			this.uiThread = Thread.CurrentThread;
			this.IsPasswordRequired = true;

			this.InitializeComponent();

			this.loading = true;
			this.ResetForm();
			this.loading = false;
			this.btnOK.Enabled = false;
			this.lblRunningUser.Text = string.Empty;

#if DEBUG
			this.lblRunningUser.Text = Environment.UserDomainName + "\\" + Environment.UserName;
#endif
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets whether or not a password is required when not using
		/// integrated authentication. Default: True;
		/// </summary>
		public bool IsPasswordRequired { get; set; }
		#endregion

		#region Methods
		/// <summary>
		/// Load the connection string builder using an MS SQL Database connection string.
		/// </summary>
		/// <param name="connectionString">The MS SQL Database connection string to load.</param>
		public void LoadData(string connectionString)
		{
			this.LoadData(new DataAccessConnection(connectionString));
		}
		/// <summary>
		/// Loads the form using an <see cref="DataAccessConnection"/>.
		/// </summary>
		/// <param name="data">The <see cref="DataAccessConnection"/> to load.</param>
		public void LoadData(DataAccessConnection data)
		{
			this.loading = true;
			this.ResetForm();
			this.SetData(data);
			this.loading = false;
			this.btnOK.Enabled = false;
		}
		/// <summary>
		/// Gets the connection information as a <see cref="DataAccessConnection"/>.
		/// </summary>
		/// <returns>A <see cref="DataAccessConnection"/>.</returns>
		public DataAccessConnection GetData()
		{
			var err = string.Empty;
			if (!this.ValidateData(ref err))
			{
				if (err.Length > 0)
					MessageBox.Show(err, "Database", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return null;
			}
			return GetDataInternal();
		}
		/// <summary>
		/// Gets the connection string
		/// </summary>
		/// <returns>The connection string</returns>
		public string GetConnectionString()
		{
			DataAccessConnection dac = this.GetData();
			if (null == dac)
				return string.Empty;
			return dac.ConnectionString;
		}
		/// <summary>
		/// Gets the connection string with the Password masked
		/// </summary>
		/// <returns>The connection string with the Password masked</returns>
		public string GetConnectionStringMasked()
		{
			DataAccessConnection dac = this.GetData();
			if (null == dac)
				return string.Empty;
			return dac.ConnectionStringMasked;
		}

		private void FormatUI()
		{
			if (this.rdoIntegratedSecurity.Checked)
			{
				this.txtUserName.Enabled = false;
				this.btnSetPwd.Enabled = false;

				if (!string.IsNullOrEmpty(this.txtServer.Text))
				{
					this.cmdTestConnectivity.Enabled = true;
					this.btnOK.Enabled = true;
					this.cboDatabase.Enabled = true;
				}
				else
				{
					this.cmdTestConnectivity.Enabled = false;
					this.btnOK.Enabled = false;
					this.cboDatabase.Enabled = false;
				}
			}
			else
			{
				this.txtUserName.Enabled = true;
				this.btnSetPwd.Enabled = true;
				if (!string.IsNullOrEmpty(this.txtServer.Text) &&
					(this.IsPasswordRequired ? !string.IsNullOrEmpty(this.password) : true) &&
					!string.IsNullOrEmpty(this.txtUserName.Text))
				{
					this.cmdTestConnectivity.Enabled = true;
					this.btnOK.Enabled = true;
					this.cboDatabase.Enabled = true;
				}
				else
				{
					this.cmdTestConnectivity.Enabled = false;
					this.btnOK.Enabled = false;
					this.cboDatabase.Enabled = false;
				}
			}
		}
		private DataAccessConnection GetDataInternal()
		{
			return new DataAccessConnection(
				this.name,
				this.rdoIntegratedSecurity.Checked,
				this.txtUserName.Text,
				this.password,
				this.txtServer.Text,
				this.cboDatabase.Text,
				this.application,
				this.workStationId,
				(int)this.connTimeout,
				(int)this.cmdTimeout);
		}
		private void ResetForm()
		{
			this.name = "newConnectionString";
			this.txtServer.Text = string.Empty;
			this.cboDatabase.Text = string.Empty;
			this.txtUserName.Text = string.Empty;

			this.password = string.Empty;
			this.application = string.Empty;
			this.workStationId = string.Empty;

			this.cmdTimeout = 60;
			this.connTimeout = 15;

			this.rdoIntegratedSecurity.Checked = true;

			this.txtConnStr.Text = string.Empty;
		}
		private void SetData(DataAccessConnection data)
		{
			if (null != data)
			{
				if (!string.IsNullOrEmpty(data.Name))
					this.Text = data.Name;
				else
					this.Text = Resources.ConnectionStringBuilder_Title;
				this.name = data.Name;
				this.txtServer.Text = data.DataSource;
				if (this.cboDatabase.Items.Contains(data.InitialCatalog))
				{
					int index = this.cboDatabase.Items.IndexOf(data.InitialCatalog);
					this.cboDatabase.SelectedIndex = index;
				}
				else
				{
					this.cboDatabase.Text = data.InitialCatalog;
				}
				this.password = data.Password;
				this.txtUserName.Text = data.UserID;
				this.application = data.ApplicationName;
				this.workStationId = data.WorkstationID;

				this.connTimeout = (decimal)data.ConnectTimeout;
				this.cmdTimeout = (decimal)data.CommandTimeout;

				this.rdoIntegratedSecurity.Checked = data.IntegratedSecurity;
				this.rdoSpecificUser.Checked = !data.IntegratedSecurity;

				this.txtConnStr.Text = data.ConnectionStringMasked;
				this.unmasked = data.ConnectionString;
			}
		}
		private bool ValidateData(ref string errorMessage)
		{
			bool IsValid;
			IsValid = true;

			if (rdoSpecifyDatabase.Checked)
			{
				#region Specified Connection Options
				if (this.rdoSpecificUser.Checked)
				{
					#region Credentials
					if (string.IsNullOrEmpty(this.password) && this.IsPasswordRequired)
					{
						errorMessage = "Must enter the credentials for this connection";
						this.btnSetPwd.Focus();
						return false;
					}
					if (string.IsNullOrEmpty(this.txtUserName.Text))
					{
						errorMessage = "Must enter the credentials for this connection";
						this.btnSetPwd.Focus();
						return false;
					}
					#endregion
				}
				if (string.IsNullOrEmpty(this.txtServer.Text))
				{
					errorMessage = "You must enter the Server Name (and/or Instance)";
					this.txtServer.Focus();
					this.txtServer.SelectAll();
					return false;
				}
				#endregion
			}
			else
			{
				#region Manual Connection String
				if (string.IsNullOrEmpty(this.txtConnStr.Text))
				{
					errorMessage = "You must enter the ConnectionString";
					this.txtConnStr.Focus();
					this.txtConnStr.SelectAll();
					return false;
				}
				#endregion
			}

			return IsValid;
		}
		private void EnumerateDatabases()
		{
			SqlDataReader reader = null;
			SqlConnection connection = null;
			SqlCommand command = null;
			this.foundDatabases = new List<object>();

			var csb = new SqlConnectionStringBuilder()
				{
					DataSource = this.GetServerName(),
					UserID = this.txtUserName.Text,
					Password = this.password,
					IntegratedSecurity = this.rdoIntegratedSecurity.Checked
				};

			try
			{
				connection = new SqlConnection(csb.ConnectionString);
				command = new SqlCommand(HSS.Forms.Resources.sqlGetDbList, connection);
				try
				{
					connection.Open();
					try
					{
						reader = command.ExecuteReader();
						while (reader.Read())
							this.foundDatabases.Add(reader.GetString(0));
					}
					catch (Exception exception2)
					{
						MessageBox.Show(HSS.Forms.Resources.List_db_name_failed + "\n" + exception2.Message, HSS.Forms.Resources.Read_database_failed, MessageBoxButtons.OK, MessageBoxIcon.Hand);
					}
				}
				catch (Exception exception)
				{
					MessageBox.Show(HSS.Forms.Resources.List_db_name_failed + "\n" + exception.Message, HSS.Forms.Resources.Connection_failed, MessageBoxButtons.OK, MessageBoxIcon.Hand);
				}
			}
			finally
			{
				try
				{
					if (reader != null)
						reader.Close();
					if (connection != null)
						connection.Close();
				}
				catch { }
			}

			if (Thread.CurrentThread == this.uiThread)
				this.PopulateDatabaseComboBox();
			else if (base.IsHandleCreated)
				base.BeginInvoke(new ThreadStart(this.PopulateDatabaseComboBox));

		}
		private void PopulateDatabaseComboBox()
		{
			if (this.cboDatabase.Items.Count == 0)
			{
				if (this.foundDatabases.Count > 0)
					this.cboDatabase.Items.AddRange(this.foundDatabases.ToArray());
				else
					this.cboDatabase.Items.Add(string.Empty);
			}
			this.dbThread = null;
		}
		private void SetConnectionOptions()
		{
			using (var dlg = new ConnectionStringOptions())
			{
				dlg.LoadData(this.application, this.workStationId, this.connTimeout, this.cmdTimeout);
				if (dlg.ShowDialog(this) == DialogResult.OK)
				{
					this.cmdTimeout = dlg.CommandTimeout;
					this.connTimeout = dlg.ConnectionTimeout;
					this.application = dlg.ApplicationName;
					this.workStationId = dlg.WorkstationId;
					this.SetData(this.GetDataInternal());
					this.FormatUI();
				}
			}
		}
		private void SetCredentials()
		{
			using (var dlg = new UserCredentials())
			{
				dlg.IsPasswordRequired = this.IsPasswordRequired;
				if (dlg.ShowDialog(this) == DialogResult.OK)
				{
					this.password = dlg.Password;
					this.txtUserName.Text = dlg.UserName;
					this.SetData(this.GetDataInternal());
					this.FormatUI();
				}
			}
		}
		private void SetConnectionString()
		{
			using (var dlg = new ConnectionStringText())
			{
				dlg.ConnectionString = this.unmasked;
				if (dlg.ShowDialog() == DialogResult.OK)
				{
					this.SetData(new DataAccessConnection(this.name, dlg.ConnectionString));
					this.FormatUI();
				}
			}
		}
		private string GetServerName()
		{
			string text = this.txtServer.Text;
			if (text == "(local)" || text == "localhost")
				text = LOCAL_MACHINE;
			return text;
		}
		private void TestConnection()
		{
			Cursor current = this.Cursor;
			this.Cursor = Cursors.WaitCursor;
			try
			{
				using (var da = new DataAccess(this.GetData()))
				{
					string strErr = string.Empty;
					if (!da.IsConnectionValid(out strErr))
						MessageBox.Show(strErr, "Failed Connnection", MessageBoxButtons.OK, MessageBoxIcon.Error);
					else
					{
						#region Get users default dabase
						try
						{
							if (da.Connection.InitialCatalog == "<default>" || da.Connection.InitialCatalog == string.Empty)
							{
								string sql = string.Format(HSS.Forms.Resources.sqlGetDefaultDb, da.Connection.UserID);
								using (var drr = da.ExecDataRowReaderSQL(sql))
								{
									if (drr.Read())
									{
										this.loading = true;
										this.cboDatabase.Text = drr["dbname"].ToString();
										this.loading = false;
									}
								}
							}
						}
						catch { }
						#endregion

						MessageBox.Show("Congratulations, you successfully connected.", "Connnection Test", MessageBoxButtons.OK, MessageBoxIcon.Information);
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Failed Connnection", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			finally
			{
				this.Cursor = current;
			}
		}

		#endregion

		#region Event Handlers

		private void cmdTestConnectivity_Click(object sender, System.EventArgs e)
		{
			this.TestConnection();
		}

		private void SetConfigurationOption(object sender, EventArgs e)
		{
			if (this.rdoSpecifyDatabase.Checked)
			{
				this.pnlBuilder.Enabled = true;
				this.cboDatabase.Enabled = true;
				this.btnEnterCnStr.Enabled = false;
			}
			else
			{
				this.pnlBuilder.Enabled = false;
				this.btnEnterCnStr.Enabled = true;
			}
			this.FormatUI();
		}
		private void SetServer(object sender, System.EventArgs e)
		{
			if (this.loading)
				return;

			this.cboDatabase.Items.Clear();
			this.FormatUI();
			this.SetData(this.GetDataInternal());
		}
		private void SetDatabase(object sender, EventArgs e)
		{
			if (this.loading)
				return;

			if ((this.cboDatabase.Items.Count == 0) && (this.dbThread == null))
			{
				this.dbThread = new Thread(new ThreadStart(this.EnumerateDatabases));
				this.dbThread.Start();
			}
			this.SetData(this.GetDataInternal());
		}
		private void EnumerateDatabases(object sender, EventArgs e)
		{
			if (this.cboDatabase.Items.Count == 0)
			{
				Cursor current = Cursor.Current;
				Cursor.Current = Cursors.WaitCursor;
				try
				{
					if ((this.dbThread == null) || (this.dbThread.ThreadState == ThreadState.Stopped))
					{
						this.EnumerateDatabases();
					}
					else if (this.dbThread.ThreadState == ThreadState.Running)
					{
						this.dbThread.Join();
						this.PopulateDatabaseComboBox();
					}
				}
				finally
				{
					Cursor.Current = current;
				}
			}
		}
		private void cboDatabase_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Down)
				this.EnumerateDatabases(sender, e);
		}
		private void cboDatabase_Leave(object sender, EventArgs e)
		{
			var control = sender as Control;
			control.Text = control.Text.Trim();
		}

		private void SetAuthenticationOption(object sender, EventArgs e)
		{
			this.cboDatabase.Items.Clear();
			if (this.rdoIntegratedSecurity.Checked)
			{
				this.password = string.Empty;
				this.txtUserName.Text = string.Empty;
			}
			this.FormatUI();
		}

		private void btnOptions_Click(object sender, EventArgs e)
		{
			this.SetConnectionOptions();
		}

		private void btnSetPwd_Click(object sender, EventArgs e)
		{
			this.SetCredentials();
		}

		private void btnOK_Click(object sender, EventArgs e)
		{
			string ErrorMessage = string.Empty;
			if (!ValidateData(ref ErrorMessage))
				MessageBox.Show(ErrorMessage, "Database", MessageBoxButtons.OK, MessageBoxIcon.Error);
			else
				this.DialogResult = DialogResult.OK;
		}

		private void btnEnterCnStr_Click(object sender, EventArgs e)
		{
			this.SetConnectionString();
		}

		private void btnAdvanced_Click(object sender, EventArgs e)
		{
			using (var properties = new ConnectionProperties())
			{
				var dac = this.GetDataInternal();
				var timeout = dac.CommandTimeout;
				properties.ConnectionString = dac.ConnectionString;
				properties.ShowDialog(this);
				if (string.IsNullOrEmpty(dac.Name))
					dac = new DataAccessConnection(properties.ConnectionString);
				else
					dac = new DataAccessConnection(dac.Name, properties.ConnectionString);
				dac.CommandTimeout = timeout;
				this.SetData(dac);
				this.FormatUI();
			}
		}

		#endregion
	}
	#endregion
}